/**
 * 2017年3月8日
 * 二人牛牛
 * 先不做错误检查
 * TODO: 做错误检查
 */
var pomelo = require('pomelo');

var async = require('async');
var wt = async.waterfall;

var consts = require('./consts');
var round = require('./round');
var utils = require('../../util/utils');

var client = pomelo.app.get('redisUser');
var mysql = require('../../dao/userDao');

var log = console.log;

/**
 * 房间状态定义： status: 0: 空闲(玩家可以加入), 1: 已满(玩家只能旁观), 2: 等待(玩家不可加入，不可旁观)
 */
function Room(rid, playerManager) {
    var self = this;
    var roomId = rid, gameId = 5;

    this.status = consts.ROOM.STATUS.FREE;  // 刚开的房间是可以加入的

    this.playerData = {};
    this.playerList = [];
    this.playerManager = playerManager;
    this.observerNum = 0;
    this.readyPlayerNum = 0;

    this.player1Time = consts.GAME.TIME.PLAYER1; // 玩家1叫庄时长
    this.player2Time = consts.GAME.TIME.PLAYER2; // 玩家2叫庄时长
    this.xiaZhuTime = consts.GAME.TIME.XIAZHU; // 下注时长
    this.kaiPaiTime = consts.GAME.TIME.KAIPAI; // 开牌时长

    this.curTimeCount = 0;  // 用来记录每一局用的时间

    this.gameStatus = consts.GAME.STATUS.UNKNOWN;
    this.checkTime = 3;// 开始前3s倒计时

    this.player = {};
    this.playerNum = 0;

    this.curRound = 0;  // 当前的游戏局数
    this.applyNextRoundPlayer = [];
    this.returnPlayer = [];

    this.curBanker = 0;
    this.lastApplyId = 0;  // 后一个申请上庄的用户ID

    this.hInterval = null; // 定时器

    this.curCards = null;
    this.curOdds = null;

    this.offlinePlayer = [];  // 掉线的玩家列表

    this.roomId = roomId;
    this.gameId = gameId;
    this.name = 'room' + roomId;
    this.channelService = pomelo.app.get('channelService');
    this.createChannel();

    log('创建新房间。rid: %s', rid);
}

var pro = Room.prototype;

/**
 *  创建房间channel
 */
pro.createChannel = function () {
    if (this.channel) {
        return this.channel;
    }
    var channelName = 'room_' + this.gameId + '_' + this.roomId;
    this.channel = this.channelService.getChannel(channelName, true);

    return this.channel;
};

// 向房间频道里添加玩家
pro.channelAdd = function (playerId, type) {
    log('向房间频道里添加玩家：', playerId);

    var player = this.playerManager.playerData[playerId];
    var serverId = player.serverId;

    this.channel.add(playerId, serverId);

    // 从大厅频道中离开    
    this.playerManager.channel.leave(playerId, serverId);

    var msg = this.playerManager.getPlayerData(playerId);
    msg.rid = this.roomId;
    msg.type = type;

    var route = type === consts.PLAYER.TYPE.PLAYER ? 'onPlayerEnter' : 'onObserverEnter';
    player.type = type;

    this.channel.pushMessage(route, msg, null);
};

// 从房间频道里删掉玩家
pro.channelLeave = function (playerId) {
    log('从房间频道里删掉玩家: ', playerId);

    var serverId = this.playerManager.playerData[playerId].serverId;

    this.channel.leave(playerId, serverId);
    this.channel.pushMessage('onPlayerLeave', { playerId }, null);
};

/**
 * 2017年3月9日
 * 进入房间
 * 1.房间中人数小于两个人，可直接进入房间
 * 2.房间中已经有两个人，如果游戏还没有开始，不能进入房间
 * 3.如果房间中有两个人，如果游戏已经开始，可以进入房间观战
 * 4.如果房间中超过2个人，可以直接进入房间
 */
pro.addPlayer = function (data, cb) {
    log('玩家进入房间！', JSON.stringify(data));

    var self = this;
    var type = consts.PLAYER.TYPE.PLAYER;  // 类型：玩家 
    var playerId = data.playerId, rid = data.rid;
    var player = this.playerManager.playerData[playerId];
    var serverId = player.serverId;
    var err = null, type = 0, msg, players;

    // 房间最多只能有两个玩家
    if (this.playerNum >= 2) {
        err = '房间已满！';
        log(err);
    }

    if (err) {
        cb(err);
    }
    else {

        this.playerNum++;

        this.channelAdd(playerId, type);

        this.playerList.push(playerId);

        players = this.getCurRoomPlayerData();

        this.playerManager.addPlayerToRoom(playerId, rid, player.coin, players, cb);
        // log('当前房间的玩家列表', this.playerList);
    }
};

// 获取当前房间的用户信息
// 先不考虑旁观者
pro.getCurRoomPlayerData = function () {
    var obj = {}, playerId;
    for (var i in this.playerList) {
        playerId = this.playerList[i];
        obj[playerId] = this.playerManager.playerData[playerId];
    }
    return obj;
};

// 用户离开房间
pro.leaveRoom = function (playerId, cb) {
    log('用户离开房间  room.leaveRoom!', playerId);
    var self = this;
    // var serverId = this.playerManager.getSid(playerId);
    var player = this.playerManager.playerData[playerId];
    var serverId = player.serverId;

    // 只有开牌时间或者 unknown 时间才能退出
    // 不是开牌时间并且不是 unknown 时间，都可以退出
    if (!this.canLeaveRoom(playerId)) {
        return cb('now is not good time to leaveRoom, wait to KAIPAI time!');
    }

    if (this.playerManager.checkPlayerRoom(playerId, this.roomId)) {
        // 如果用户在这个房间里
        if (player.type === consts.PLAYER.TYPE.PLAYER) {
            // 如果用户是玩家，递减用户数量
            // this.playerNum--;
            // this.readyPlayerNum--;
            this.kickPlayerListById(playerId);
            delete player.prepare;
        }

        // 用户离开房间频道
        this.channelLeave(playerId);
        // 回到大厅频道中
        this.playerManager.channel.add(playerId, serverId);

        log('当前房间的玩家列表', this.playerList);

        this.playerManager.kickPlayerFromRoom(playerId, cb);
    }
    else {
        cb('player is not in this room!');
    }
};

// 检查是否可以退出房间
pro.canLeaveRoom = function (playerId) {
    log('检查是否可以离开房间：', playerId);

    if (this.playerList.indexOf(playerId) === -1) {
        return true;
    }

    if (this.getGameStatus().gameStatus !== consts.GAME.STATUS.KAIPAI && this.getGameStatus().gameStatus !== consts.GAME.STATUS.UNKNOWN) {
        return false;
    }
    else {
        return true;
    }
};

// 用户掉线
pro.kickGame = function (playerId, cb) {
    log('用户掉线: ', playerId);

    // var self = this;
    // var serverId = this.playerManager.getSid(playerId);
    // var player = this.playerManager.playerData[playerId];
    // var serverId = player.serverId;

    // 1.非开牌时间，记录玩家ID；2.开牌时间，直接下线    
    if (!this.canLeaveRoom(playerId)) {
        log('暂时放到离线用户列表 ', playerId);
        // 如果不是开牌时间并且掉线玩家列表中没有这个用户
        if (this.offlinePlayer.indexOf(playerId) == -1) {
            this.offlinePlayer.push(playerId);
        }
        cb(null);
    }
    else {
        this.leaveRoom(playerId, cb);
        this.playerManager.kickPlayer(playerId);
    }
};

// 房间添加观察者
pro.addObserver = function (msg, cb) {
    log('房间添加观察者: ', msg.playerId);

    var type = consts.PLAYER.TYPE.OBSERVER;  // 类型：观察者
    var playerId = msg.playerId;
    var player = this.playerManager.playerData[playerId];
    // var sid = player.serverId, playerName = player.playerName, coin = player.coin;
    var coin = player.coin;

    if (this.playerManager.checkPlayerRoom(playerId)) {
        return cb('player ' + playerId + ' have already in room!');
    }

    this.channelAdd(playerId, type);

    this.observerNum++;

    msg.coin = coin;
    this.playerManager.addObserver(msg, cb);
};

// 游戏准备
pro.gameReady = function (msg, cb) {
    log('游戏准备！room.gameReady: ', playerId);
    // log('当前房间的玩家列表', this.playerList);

    var playerId = msg.playerId;
    var player = this.playerManager.playerData[playerId];

    if (!player) {
        return cb('player ' + playerId + ' have not login!');
    }

    if (player.prepare) {
        return cb('player ' + playerId + ' have already prepared!');
    }
    else if (this.playerList.indexOf(playerId) === -1) {
        return cb('player ' + playerId + ' is not this room player!');
    }
    else {
        this.readyPlayerNum++;
        player.prepare = 1;
        this.channel.pushMessage('onPlayerReady', { playerId });

        this.checkGameReady();

        this.playerManager.gameReady(msg, cb);
    }
};

// 检查游戏准备状态
pro.checkGameReady = function () {
    log('检查游戏准备人数：', this.readyPlayerNum);
    var self = this;

    if (this.readyPlayerNum === 2 && !this.hInterval) {
        this.hInterval = setInterval(this.OnTimer, 1000, this);
    }
};

// 获取下注的数组
pro.getBidList = function () {
    log('获取下注数组!');
    var coin,
        coin0 = this.playerManager.playerData[this.playerList[0]].coin,
        coin1 = this.playerManager.playerData[this.playerList[1]].coin;

    coin = coin0 < coin1 ? coin0 : coin1;

    function getBid(num) {
        return Math.floor(coin / num);
    }

    return [getBid(5), getBid(10), getBid(15), getBid(20)];
};

// 检查用户角色 
pro.checkRole = function (type, playerId) {
    log('检查用户角色！');

    if (!this.playerData[playerId]) {
        return false;
    }

    if (type === 'player') {
        if (this.playerData[playerId].type !== consts.PLAYER.TYPE.PLAYER) {
            return false;
        }
    }
    else if (type === 'observer') {
        if (this.playerData[playerId].type !== consts.PLAYER.TYPE.OBSERVER) {
            return false;
        }
    }

    return true;
};

// 房间事件循环
pro.OnTimer = function (self) {
    var gameStatus = consts.GAME.STATUS;

    if (self.gameStatus === gameStatus.PLAYER1) {
        // 玩家1决定是否上庄
        self.ProcessCheckPlayer1();
    }
    else if (self.gameStatus === gameStatus.PLAYER2) {
        // 玩家2决定是否上庄
        self.ProcessCheckPlayer2();
    }
    else if (self.gameStatus === gameStatus.XIAZHU) {
        // 玩家下注
        self.ProcessCheckXiaZhu();
    }
    else if (self.gameStatus === gameStatus.KAIPAI) {
        // 开牌时间
        self.ProcessCheckKaiPai();
    }
    else {
        // 游戏开始前的准备时间
        console.log('UNKNOWN TIME!', self.getGameStatus());
        self.checkTime--;
        if (self.checkTime === 0) {
            self.loadCardConfig(function () {
                log('获取牌局结果完成！');
                self.SetGameStatus(consts.GAME.STATUS.PLAYER1, self.player1Time);
                var ret = self.getGameStatus();
                ret.applyId = self.getApplyId();
                self.channel.pushMessage('onPlayer1Apply', ret, null);
                log('======================================change gameStatus Player1Apply: ', ret);
            });
        }
    }

};

// 玩家1叫庄时间！
// 如果没有选择，就认为是没有申请上庄
Room.prototype.ProcessCheckPlayer1 = function () {
    log('玩家1叫庄时间！');

    var self = this;
    self.checkTime--;

    log('checkPlayer1 self.checkTime', self.checkTime);

    if (self.checkTime <= 0) {
        this.nextGameStatus();
        var ret = this.getGameStatus();

        ret.applyId = self.getApplyId();
        self.lastApplyId = ret.applyId;
        this.channel.pushMessage('onPlayer2Apply', ret, null);
        log('====================================change gameStatus checkPlayer2: ');
    }
};

// 玩家2叫庄时间！
Room.prototype.ProcessCheckPlayer2 = function () {
    log('玩家2叫庄时间！');

    var self = this;
    self.checkTime--;

    if (this.curBanker) {
        log('本局的庄已经确定了！');
        this.checkTime = 0;
    }

    log('checkPlayer2 self.checkTime', self.checkTime);

    if (self.checkTime <= 0) {

        this.nextGameStatus();

        if (!this.curBanker) {
            // 后选择的人坐庄
            this.curBanker = self.lastApplyId;
        }

        var idx = this.playerList.indexOf(this.curBanker);
        this.curPlayer = idx === 0 ? this.playerList[1] : this.playerList[0];

        // 玩家1申请上庄时就已经load了牌局
        var banker = self.curBanker;
        log('当前的庄家是：', banker);
        self.channel.pushMessage('onSetBanker', { banker, bidList: self.getBidList() }, null);

        log('==================================== change gameStatus xiazhu: ');
    }
};

// 下注时间
Room.prototype.ProcessCheckXiaZhu = function () {
    var self = this;
    self.checkTime--;
    log('XiaZhu self.checkTime', self.checkTime);

    if (self.checkTime === 0) {  // 转为开牌
        // 只用来计算

        console.log('当前的下注信息：', this.bidData);
        if (!this.bidData.bid) {
            // 如果玩家没有选择下注
            this.bidData.playerId = this.curPlayer;
            this.bidData.bid = this.getBidList()[3];  // 默认下最小的注
            // this.bidData.bid = this.curRound % 2 === 0 ? 110 : 120;  // 暂时先下1.2元
        }

        this.channel.pushMessage('onBid', this.bidData, null);

        self.SetGameEnd(function (err, ret) {
            if (!err) {
                self.curRound++;
                // 这样就可以确定只在checkTime = 0 的时候执行一次
                console.log('结算完成,发出广播 in setGameEnd cb! >>>>>>>>>>>>>>>>>>');
                console.log(ret);
                self.channel.pushMessage('onCalcResult', ret, null);
                self.playerManager.pushCalcResult(ret);
                self.curBanker = null;  // 初始化庄信息

                // 检查玩家剩余金钱
                self.checkPlayerCoin();
            }
            else {
                console.warn(err);
                throw new Error(err);
            }
        });
    }

    if (self.checkTime <= 0) {
        // 等于0时就已经转换到下一个状态，转换状态和结算分离开
        this.nextGameStatus();

        var ret = self.getGameStatus();
        self.channel.pushMessage('onGameKaiPai', ret, null);
        log('======================================change gameStatus kaiPai: ', ret);
    }
};

// 开牌时间
Room.prototype.ProcessCheckKaiPai = function () {
    var self = this;
    self.checkTime--;
    log('KaiPai self.checkTime', self.checkTime);

    // 首先检查是否有人掉线
    if (self.offlinePlayer.length > 0) {
        // TODO: 掉线玩家重新登录
        log('掉线玩家列表：', self.offlinePlayer);
        // self.stopGame();
        for (let i in self.offlinePlayer) {
            let playerId = self.offlinePlayer[i];
            log('掉线玩家: ', playerId);
            // 将每个掉线的用户从房间和大厅的频道和数据里面删掉
            self.kickPlayerListById(playerId);
            self.playerManager.kickPlayerFromRoom(playerId, function () { });
            if (self.returnPlayer.indexOf(playerId) === -1) {
                // 只有没有返回游戏的玩家，才可以从玩家管理列表中删掉
                // 如果已经返回游戏，那么就只从房间列表中删掉
                self.playerManager.kickPlayer(playerId);
            }
        }
        self.offlinePlayer = [];
        self.returnPlayer = [];
        // return; // 停止执行后面的程序
    }

    if (self.checkTime <= 0) {
        log('切换到玩家1申请坐庄状态！');
        // 获取了牌局设置之后才能转到玩家1叫庄时间
        this.loadCardConfig(function () {
            log('获取牌局结果完成！');
            self.SetGameStatus(consts.GAME.STATUS.PLAYER1, self.player1Time);
            var ret = self.getGameStatus();
            ret.applyId = self.getApplyId();
            self.channel.pushMessage('onPlayer1Apply', ret, null);
            self.applyNextRoundPlayer = [];  // 初始化申请下一轮的用户信息
            log('======================================change gameStatus Player1Apply: ', ret);
        });
    }
};

// 检查玩家的金钱
pro.checkPlayerCoin = function () {
    log('检查玩家的金钱');

    var playerId, playerData = this.playerManager.playerData;

    log(playerData);

    for (let idx in this.playerList) {
        playerId = this.playerList[idx];
        if (playerData[playerId].coin <= 100) {
            log('玩家 ' + playerId + ' 金钱不足!');
            this.offlinePlayer.push(playerId);
        }
    }
};

// 获取申请上庄的用户id
pro.getApplyId = function () {
    log('获取申请上庄的用户id!');

    var gameStatus = this.getGameStatus().gameStatus;
    var id;

    // log(this.curRound, gameStatus, this.playerList);

    // 变量： 1.round 2.status
    // status === PLAYER1
    // round === 0, id = 1; round === 1, id = 2
    // status === PLAYER2
    // round === 0, id = 2; round === 1, id = 1

    if (gameStatus === consts.GAME.STATUS.PLAYER1) {
        if (this.curRound % 2 === 0) {
            id = this.playerList[0];
        }
        else {
            id = this.playerList[1];
        }
    }
    else if (gameStatus === consts.GAME.STATUS.PLAYER2) {
        if (this.curRound % 2 === 1) {
            id = this.playerList[0];
        }
        else {
            id = this.playerList[1];
        }
    }
    else {
        console.error('获取申请上庄的用户id时间不对！');
        // JSON.parse('a');
        throw new Error('time invalid!');
    }

    return id;
};

// 读取配置的牌
pro.loadCardConfig = function (cb) {
    var self = this;

    var key = 'gameCards' + this.gameId,
        reCards;

    client.lrange(key, 0, -1, function (err, rel) {
        if (err) {
            console.log(err);
            console.warn('redis错误！');
        }
        else {
            if (rel.length <= 4) {
                log(rel);
                console.warn('设置的牌少于4张，清空换的牌！');
                reCards = [];
            }
            else {
                reCards = rel;
            }

            // reCards = ['C10', 'A10', 'X1', 'A10', 'D12', 'A2', 'D2', 'A6', 'C2', 'A2'];  //  炸弹，炸弹
            round.replaceCards(reCards);

        }
        self.RunCardRound(cb);
    });
};

/**
 * 运行牌局
 * 需要确定庄家和玩家都是谁
 */
pro.RunCardRound = function (cb) {
    var self = this;

    self.bidCount = 0;

    // 获取此局结果
    var rel = round.next(this.roomId);
    self.curRoundRel = rel;
    log('此局结果: ', rel);

    this.curCards = rel.player[0].cards.join() + '_' + rel.player[0].cards.join();
    this.curOdds = rel.odd;
    this.cardResult = rel.player[0].rel.str.split(',')[0] + ',' + rel.player[1].rel.str.split(',')[0];

    this.curResult = {};
    for (let i in rel.player) {
        if (!this.curResult[i]) {
            this.curResult[i] = {};
        }
        this.curResult[i].cards = rel.player[i].cards;
        this.curResult[i].rel = {
            card: rel.player[i].rel.card, str: rel.player[i].rel.str
        };
    }
    log(this.cardResult);
    log(this.curResult);

    this.bidData = {};
    cb();
};

// 游戏结束
pro.SetGameEnd = function (cb) {
    console.log('before gameEnd callback! >>>>>>>>>>>>>>>>');
    var self = this;

    var ret = self.curResult;
    self.channel.pushMessage('onGameOver', { ret }, null);

    self.CalcResult(self.curOdds, cb);
};

// 计算结果
pro.CalcResult = function (curOdds, cb) {
    var self = this;
    console.log('Room.CalcResult caught >>>>>>>>>>>>>>>>>');
    console.log(curOdds);
    // curOdds = { '0': 2 };

    var bankerId = this.curBanker;
    var playerId = this.curPlayer;

    var playerData = this.playerManager.playerData;
    var player = playerData[playerId], banker = playerData[bankerId];

    var bidNum = this.bidData.bid, odd = curOdds[0];

    var tax = consts.ROOM.TAX, taxSum = 0;
    // tax = 0;

    log('玩家 %s, 庄家 %s', playerId, bankerId);
    log(player, banker);

    var p = {}, b = {};

    if (odd > 0) {
        // 也就是玩家赢，庄家输
        p.coinGain = bidNum * odd;
        p.tax = Math.floor(tax <= 0 ? 0 : p.coinGain * tax / 100);  // 需要是整数分
        p.coinChange = p.coinGain - p.tax;
        b.coinChange = -p.coinGain;
        b.tax = 0;
    }
    else {
        b.coinGain = -bidNum * odd;  // 加个负号才是正数
        b.tax = Math.floor(tax <= 0 ? 0 : b.coinGain * tax / 100);
        b.coinChange = b.coinGain - b.tax;
        p.coinChange = -b.coinGain;
        p.tax = 0;
    }

    taxSum = b.tax + p.tax;

    log('p, b', p, b);

    player.coin += p.coinChange;
    banker.coin += b.coinChange;

    log('banker, player ', banker, player);

    var ret = {};
    // var ret = {
    //     playerId: {
    //         coinChange: p.coinChange,
    //         tax: p.tax
    //     },
    //     bankerId: {
    //         coinChange: b.coinChange,
    //         tax: b.tax 
    //     }
    // };
    ret[playerId] = { coinChange: p.coinChange, tax: p.tax };
    ret[bankerId] = { coinChange: b.coinChange, tax: b.tax };

    self.recordResult(ret, taxSum, cb);
};

// 记录结果
pro.recordResult = function (ret, taxSum, cb) {
    log('记录结果');
    var self = this;

    var getYuan = utils.getYuan;

    var bankerId = this.curBanker;
    var playerId = this.curPlayer;
    var playerData = this.playerManager.playerData;
    var player = playerData[playerId], banker = playerData[bankerId];

    var gameId = this.gameId, roomName = this.name, bankerName = banker.playerName, curCards = this.curCards, cardResult = this.cardResult, userCount = 2, bidCoin = this.bidData.bid;

    var sql = 'INSERT INTO game_round (gameId, roomName, userId, userName, card, cardResult, userCount, bidCoin, tax, platformProfit, duration, addTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())';
    var args = [gameId, roomName, bankerId, bankerName, curCards, cardResult, userCount, getYuan(bidCoin), getYuan(taxSum), getYuan(taxSum), self.xiaZhuTime];

    mysql.query(sql, args, function (err, rows) {
        if (err) {
            log(err);
            return cb(err, {});
            throw new Error('caught exception when calcResult in playerManager.js');
        }
        insertId = rows.insertId;
        var coinField = 'coin';
        async.each(self.playerList, function (playerId, callback) {
            log('当前处理的用户：', playerId);
            var player = self.playerManager.playerData[playerId];
            var playerName = player.playerName;
            var curBidCoin = playerId === self.curBanker ? 'banker' : bidCoin;
            var coinChange = ret[playerId].coinChange, taxCoin = ret[playerId].tax;
            var result = coinChange > 0 ? 1 : -1;
            log(curBidCoin, coinChange, result);
            wt([function (wcb) {
                var sql = 'insert into game_queue (gameId, userId, userName, bidRecord, coinChange, tax, result, roundId, addTime) values (?, ?, ?, ?, ?, ?, ?, ?, now())';
                var args = [gameId, playerId, playerName, curBidCoin, getYuan(coinChange), getYuan(taxCoin), result, insertId];

                mysql.query(sql, args, function (err, rows) {
                    if (err) {
                        console.error(err);
                        wcb(err);
                        throw new Error('caught exception when calcResult in playerManager.js');
                    }
                    else {
                        wcb(null);
                    }
                });
            },
            function (wcb) {
                client.hget(playerId, coinField, function (err, rel) {
                    if (err) {
                        console.error(err);
                        wcb(err);
                        return;
                    }
                    if (rel !== null) {
                        // 没有更新，可直接使用
                        wcb(null, 0);
                    }
                    else {
                        // 其它地方更新了
                        var sql = 'SELECT (coin + (SELECT ifnull(sum(coinChange), 0) coinChange FROM game_queue WHERE userId = ? AND isDone = 0) ) as coin FROM game_user where userId = ?';
                        var args = [playerId, playerId];
                        mysql.query(sql, args, function (err, rows) {
                            if (err) {
                                log(err);
                                return wcb(err);
                            }
                            player.coin = rows[0].coin * 100;
                            wcb(null, 1);
                        });
                    }
                });
            },
            function (type, wcb) {
                // 重新设置或者递增缓存
                log('重新设置缓存', playerId, player.coin);
                client.hset(playerId, coinField, getYuan(player.coin), function (err, rel) {
                    if (!err) {
                        ret[playerId].coin = player.coin;
                        return wcb(null);
                    }
                    else {
                        console.error(err);
                        return wcb(err);
                    }
                });
            }
            ], function (err) {
                if (!err) {
                    return callback(null);
                }
                else {
                    console.error(err);
                    callback(err);
                }
            });
        },
            function (err) {
                if (err) {
                    console.error(err);
                    cb(err, {});
                    throw new Error('caught exception when calcResult in playerManager.js');
                }
                else {
                    log('结算成功');
                    log(ret);
                    cb(null, ret);
                }
            });
    });
};

// 记录下注
pro.bid = function (msg) {
    log('记录下注信息！');

    if (this.playerList.indexOf(msg.playerId) === -1) {
        return 'player ' + playerId + ' is not this room player!';
    }

    // 检查是否是下注时间
    if (!this.checkGameStatus('xiazhu')) {
        return 'now is not bid time!';
    }

    var playerId = msg.playerId;

    if (playerId === this.curBanker || this.playerList.indexOf(playerId) === -1) {
        log('下注玩家 ' + playerId + ' 有误!', this.curBanker, this.playerList);
        return 'this bid player ' + playerId + ' is invalid!';
    }

    this.bidData = { playerId, bid: msg.bid };
    // 下注成功时发出一个广播
    this.channel.pushMessage('onBid', this.bidData, null);

    this.checkTime = 1;

    return null;
};

// 玩家决定是否上庄！
// 顺序判断需要！
pro.applyBanker = function (msg, cb) {
    log('决定是否上庄！');
    var self = this;

    if (!this.checkGameStatus('applyBanker')) {
        return cb('now is not applyBanker time!');
    }

    // 1.判断玩家id是否正确；2.记录玩家是否叫庄
    var playerId = parseInt(msg.playerId);

    log(playerId, this.getApplyId());
    if (playerId !== this.getApplyId()) {
        return cb('now is not player ' + playerId + ' turn to apply banker!');
    }

    if (msg.apply) {
        // 如果申请坐庄，则进入到下注阶段
        this.curBanker = playerId;

        var idx = this.playerList.indexOf(this.curBanker);
        this.curPlayer = idx === 0 ? this.playerList[1] : this.playerList[0];

        this.channel.pushMessage('onSetBanker', { banker: playerId, bidList: self.getBidList() }, null);

        this.SetGameStatus(consts.GAME.STATUS.XIAZHU, this.xiaZhuTime);
    }
    // TODO: 如果玩家1申请不上庄，那么就直接设置玩家2为庄
    else {
        this.checkTime = 0;
    }

    cb(null);
};

// 从玩家列表里删掉玩家
pro.kickPlayerListById = function (playerId) {
    log('kickPlayerListById 从玩家列表里删掉玩家: ', playerId);

    for (var i in this.playerList) {
        if (this.playerList[i] === playerId) {
            this.playerList.splice(i, 1);

            this.playerNum--;
            this.readyPlayerNum--;

            this.channelLeave(playerId);
        }
    }

    log(this.playerList);

    if (this.playerList.length < 2) {
        this.stopGame();
    }
};

// 停止游戏
pro.stopGame = function () {
    log('停掉游戏循环!');
    // 停掉游戏循环
    clearInterval(this.hInterval);
    this.hInterval = null;
    // 恢复游戏未开始状态
    this.gameStatus = consts.GAME.STATUS.UNKNOWN;
    this.checkTime = 3;// 开始前3s倒计时
};

// 提前结束开牌阶段，进入到下一轮
pro.nextRound = function (msg, cb) {
    log('二人牛牛 roomManager 进入到下一轮！');
    var playerId = msg.playerId;
    var list = this.applyNextRoundPlayer;

    if (this.getGameStatus().gameStatus !== consts.GAME.STATUS.KAIPAI) {
        return cb('now is not apply nextRound time!');
    }

    if (this.playerList.indexOf(playerId) > -1) {
        // 如果在这个房间里
        if (list.indexOf(playerId) === -1) {
            // 如果还没有申请结束
            list.push(playerId);
        }
        else {
            return cb('player ' + playerId + ' have already apply nextRound!');
        }
    }
    else {
        return cb('player ' + playerId + ' is not this room player!');
    }

    if (list.length === 2) {
        log('两个人已经都选择了下一轮！');
        this.checkTime = 0;
    }

    cb(null);
};

// 设置游戏状态
pro.SetGameStatus = function (status, time) {
    var self = this;
    self.gameStatus = status;
    self.checkTime = time;
};

// 获取游戏状态
pro.getGameStatus = function () {
    var self = this;
    var ret = {
        gameStatus: self.gameStatus,
        checkTime: self.checkTime
    };
    return ret;
};

// 检查房间的游戏状态
pro.checkGameStatus = function (str) {
    log('检查房间状态！', str);
    var status = consts.GAME.STATUS;
    var gameStatus = this.getGameStatus().gameStatus;
    switch (str) {
        case 'player1': return gameStatus === status.PLAYER1; break;
        case 'player2': return gameStatus === status.PLAYER2; break;
        case 'applyBanker': return gameStatus === status.PLAYER1 || gameStatus === status.PLAYER2; break;
        case 'xiazhu': return gameStatus === status.XIAZHU; break;
        case 'kaipai': return gameStatus === status.KAIPAI; break;
        default: log(str); throw new Error(str);
    }
};

// 切换游戏状态
pro.nextGameStatus = function () {
    log('切换到下一个游戏状态');
    var self = this;

    var STATUS = consts.GAME.STATUS;
    var status = [STATUS.PLAYER1, STATUS.PLAYER2, STATUS.XIAZHU, STATUS.KAIPAI];
    var time = [this.player1Time, this.player2Time, this.xiaZhuTime, this.kaiPaiTime];

    var gameStatus = this.getGameStatus().gameStatus;
    var idx = parseInt(gameStatus);

    if (gameStatus !== STATUS.KAIPAI) {
        // 开牌时的和其它不同，而且需要用到异步的，因此不写在这里
        this.SetGameStatus(status[idx], time[idx]);
    }
};

module.exports = Room;
